﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Reflection;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Windows.Forms;

namespace Ling.P2P.Provider
{
    /// <summary>
    /// 用于实现 P2P 的实体。
    /// </summary>
    public abstract class TcpP2PEntry
    {
        private AddressFamily _UseAddressFamily = AddressFamily.InterNetwork;
        protected IPEndPoint _LocalIpEndPoint;
        private int _LocalPort;
        private IPAddress _LocalIP;
        private int _MaxBufferLength = 2048;
        protected P2PMessage? CurrentMessage;
        protected string ReceiveingFileName;
        protected Socket MainSocket;
        protected bool _StopListen = true;
        private bool EnforceCloseSkt = false;
        protected long ReceivedBytes = 0x0;

        #region 初始化部份

        protected TcpP2PEntry()
        {
        }

        /// <summary>
        /// 从指定的 IP 地址簇中获得指定版本的 IP 地址信息。
        /// </summary>
        /// <param name="Addresses">IP 地址簇。</param>
        /// <returns></returns>
        protected IPAddress GetIPAddressByFamily(IPAddress[] Addresses)
        {
            foreach (IPAddress addr in Addresses)
            {
                if (addr.AddressFamily == UseAddressFamily)
                    return addr;
            }
            return null;
        }

        #endregion

        #region 属性定义

        /// <summary>
        /// 获取或设置该 P2PEntry 所要使用的 IP 协议。
        /// </summary>
        public AddressFamily UseAddressFamily
        {
            get { return _UseAddressFamily; }
            set { _UseAddressFamily = value; }
        }

        /// <summary>
        /// 获用于侦听来自服务端的消息的本地IP及端口信息。
        /// </summary>
        public IPEndPoint LocalIpEndPoint
        {
            get { return _LocalIpEndPoint; }
        }

        /// <summary>
        /// 用于初始化P2P实例的要地端口号。
        /// </summary>
        public int LocalPort
        {
            get { return _LocalPort; }
            set { _LocalPort = value; }
        }

        /// <summary>
        /// 用于接收某些未知数据大小的最大缓冲区量。
        /// </summary>
        public int MaxBufferLength
        {
            get { return _MaxBufferLength; }
            set { _MaxBufferLength = value; }
        }

        /// <summary>
        /// 获取或设置一个值，该址指示是否停止该 P2PEntry 的侦听状态。
        /// </summary>
        public bool StopListen
        {
            get { return _StopListen; }
            set 
            { 
                _StopListen = value;
                if (_StopListen && MainSocket != null && MainSocket.IsBound)
                {
                    EnforceCloseSkt = true;
                    if(MainSocket.Available > 0)
                        MainSocket.Shutdown(SocketShutdown.Both);
                    MainSocket.Close();
                    MainSocket.Dispose();
                    MainSocket = null;
                }
            }
        }

        /// <summary>
        /// 获取该 P2PEntry 的类型。
        /// </summary>
        public virtual P2PEntryType EntryType
        {
            get { return P2PEntryType.Unknown; }
        }

        /// <summary>
        /// 获取或设置本地 IP 地址（仅在 P2PEntry 类型为 P2PServer 时才能设置）。
        /// </summary>
        public IPAddress LocalIP
        {
            get { return _LocalIP; }
            set
            {
                if (EntryType != P2PEntryType.P2PServer)
                    throw new Exception("只有 P2PEntry 类型为 P2PServer 时才能设置 LocalIP 属性。");
                _LocalIP = value;
            }
        }

        #endregion

        #region 事件定义

        /// <summary>
        /// 当收到数据时激发该事件。
        /// </summary>
        public event ReceiveEventHandler ReceiveData;

        /// <summary>
        /// 触发 ReceiveData 事件上。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnReceiveData(ReceiveDataEventArgs e)
        {
            if (ReceiveData != null)
                ReceiveData(this, e);
        }

        /// <summary>
        /// 当收到文件传送请求时发生。
        /// </summary>
        public event ReceiveFileEventHandler RequestFileTrans;

        /// <summary>
        /// 触发 RequestFileTrans 事件。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnRequestFileTrans(ReceiveFileEventArgs e)
        {
            if (RequestFileTrans != null)
                RequestFileTrans(this, e);
        }

        /// <summary>
        /// 在文件传输过程中,接收完一部份数据时发生。
        /// </summary>
        public event ReceivingFileEventHandler ReceivingFile;

        /// <summary>
        /// 触发 ReceivingFile 事件。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnReceivingFile(ReceivingFileEventArgs e)
        {
            if (ReceivingFile != null)
                ReceivingFile(this, e);
        }

        /// <summary>
        /// 文件传输完成时发生。
        /// </summary>
        public event ReceiveFileEventHandler ReceiveFileComplete;

        /// <summary>
        /// 触发 ReceiveFileComplete 事件。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnReceiveFileComplete(ReceiveFileEventArgs e)
        {
            if (ReceiveFileComplete != null)
                ReceiveFileComplete(this, e);
        }

        /// <summary>
        /// 在文件传输过程中,接收完一部份数据时发生。
        /// </summary>
        public event ReceivingFileEventHandler SendingFile;

        /// <summary>
        /// 触发 ReceivingFile 事件。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnSendingFile(ReceivingFileEventArgs e)
        {
            if (SendingFile != null)
                SendingFile(this, e);
        }

        /// <summary>
        /// 文件传输完成时发生。
        /// </summary>
        public event ReceiveFileEventHandler SendFileComplete;

        /// <summary>
        /// 触发 ReceiveFileComplete 事件。
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnSendFileComplete(ReceiveFileEventArgs e)
        {
            if (SendFileComplete != null)
                SendFileComplete(this, e);
        }

        #endregion

        #region 侦听及数据接收处理

        /// <summary>
        /// 侦听程序收到数据时的处理。
        /// </summary>
        /// <param name="skt"></param>
        protected virtual void AcceptData(Socket skt)
        { 
            // 子类实现。
        }

        /// <summary>
        /// 开始侦听，并接收数据。
        /// </summary>
        public void ListenReceive()
        {
            if (!StopListen) return; // 如果已经在侦听则不要在执行。
            StopListen = false;
            if (MainSocket == null)
                MainSocket = new Socket(UseAddressFamily, SocketType.Stream, ProtocolType.Tcp);
            MainSocket.Bind(LocalIpEndPoint);
            Thread ListenThd = new Thread(new ThreadStart(delegate 
            {
                try
                {
                    while (!StopListen)
                    {
                        MainSocket.Listen(0);
                        Socket skt = MainSocket.Accept();
                        P2PMessage? m = ReceiveMessage(skt);
                        if (m != null) // 消息解释不正确时不作处理。
                        {
                            CurrentMessage = m;
                            // 为提高侦听程序的响应效率，收到数据后将处理定向到另一个线程去执行。
                            Thread thd = new Thread(new ThreadStart(delegate { AcceptData(skt); }));
                            thd.Start();
                        }
                    }
                    if (!EnforceCloseSkt) // 在没有强制关闭 Socket 的时候才能够执行此释放。
                    {
                        MainSocket.Close();
                        MainSocket.Dispose();
                        MainSocket = null;
                    }
                }
                catch (Exception Ex)
                {
                    if (!EnforceCloseSkt)
                    {   // 在没有强制结束的情况下异常，就执行重试。
                        MessageBox.Show(Ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        ListenReceive();
                    }
                }
            }));
            ListenThd.Start();
        }

        /// <summary>
        /// 接收已知大小的数据。
        /// </summary>
        /// <param name="skt"></param>
        /// <param name="receiveLen"></param>
        /// <returns></returns>
        public byte[] ReceiveKnownData(Socket skt, int receiveLen)
        {
            byte[] buf = new byte[receiveLen];
            int len = 0;
            while (len < receiveLen)
            {
                len += skt.Receive(buf, len, receiveLen - len, SocketFlags.None);
                if (CurrentMessage != null && CurrentMessage.Value.MessageType == P2PConstants.MSG_SEND_FILE)
                {
                    ReceivedBytes += len;
                    OnReceivingFile(new ReceivingFileEventArgs(CurrentMessage.Value.ContentLength, ReceivedBytes));
                }
            }
            return buf;
        }

        /// <summary>
        /// 接收消息。
        /// </summary>
        /// <param name="skt"></param>
        protected P2PMessage? ReceiveMessage(Socket skt)
        {   // 先接收第一个字节判断发送的内容是不是消息。如果不是则不做处理。
            byte[] buf = new byte[1];
            int len = skt.Receive(buf, 0, 1, SocketFlags.None);
            if (len == 1 && buf[0] == P2PConstants.HEADER_IS_MSG)
            {   // 如果是消息则将后继的内容全部接收然后得到消息结构。
                buf = null;
                buf = ReceiveKnownData(skt, P2PMessage.GetSize());
                P2PMessage Msg = P2PMessageConverter.GetMessage(buf);
                return Msg;
            }
            else
            {
                return null;
            }
        }


        #endregion

        #region 数据发送处理

        /// <summary>
        /// 向目标 Socket 发送指定的内容。
        /// </summary>
        /// <param name="data">要发送的内容。</param>
        /// <param name="skt">目标 Socket 对象。</param>
        /// <param name="doEvent">为 true 时将触发 SendingFile 事件。</param>
        /// <returns></returns>
        protected int SendData(byte[] data, Socket skt, bool doEvent)
        {
            // 内容重组完成后发送之。
            int slen = 0;
            while (slen < data.Length)
            {
                slen += skt.Send(data, slen, data.Length - slen, SocketFlags.None);
                if (doEvent)
                    OnSendingFile(new ReceivingFileEventArgs(data.Length, (long)slen));
            }
            return slen;
        }

        /// <summary>
        /// 向指定的目标主机发送消息。
        /// </summary>
        /// <param name="Message">要发送的消息结构。</param>
        /// <param name="IpOrHostName"></param>
        public Socket SendMessage(P2PMessage Message, IPEndPoint TargetEndPoint)
        {
            byte[] msg_data = P2PMessageConverter.GetBytes(Message);
            byte[] data = new byte[msg_data.Length + 1];
            data[0] = P2PConstants.HEADER_IS_MSG;
            int dIndex = 1;
            for (int i = 0; i < msg_data.Length; ++i)
            {
                data[dIndex] = msg_data[i];
                dIndex++;
            }
            Socket skt = new Socket(UseAddressFamily, SocketType.Stream, ProtocolType.Tcp);
            skt.Connect(TargetEndPoint);
            SendData(data, skt, false);
            return skt;
        }

        #endregion

        /// <summary>
        /// 获取大数据需要传输的次数。
        /// </summary>
        /// <param name="eachLen">每次传输的字节量。</param>
        /// <param name="contentLen">数据的总体大小。</param>
        /// <returns></returns>
        protected int GetTransferCount(int eachLen, long contentLen)
        {
            if ((contentLen % eachLen) == 0)
                return (int)(contentLen / eachLen);
            else
                return ((int)(contentLen / eachLen)) + 1;
        }
    }
}
